#ifndef XGE_XSMART_PTR_H_
#define XGE_XSMART_PTR_H_
#include "xge_common_config.h"
#include "xallocator.h"
#include "xallstatic.h"
#include "xatomic_operation.h"
#include <algorithm>
namespace xge {
namespace xge_internal {

  // Atomic operation policy
  class ThreadSafeOperationPolicy : public AllStatic {
  public:
    typedef size_t ReferenceType;
    typedef ReferenceType* OperationTypePtr;
  public:
    // For the specific concept required above
    static void AddByOne ( OperationTypePtr dest_ptr ) {
        AtomicOperation::AtomicIncrement( ( AtomicOperation::OperationTypePtr)(dest_ptr) );
    }
    // For the specific concept required above
    static bool SubByOne (  OperationTypePtr dest_ptr ) {
        XASSERT_SAY((*dest_ptr) !=0, _xT("The reference is 0 , however calling SubByOne function!"));
        return AtomicOperation::AtomicDecrement( ( AtomicOperation::OperationTypePtr)(dest_ptr) ) 
          == (AtomicOperation::Atomic_t)(0);
    }
    static void SetRef( OperationTypePtr dest_ptr , const ReferenceType& val ) {
      *dest_ptr = val ;
    }
    static bool EmptyReference( const ReferenceType& ref ) {
      return LoadAndAcquire(ref) == (ReferenceType)(0);
    }
  };

  class CommonOperationPolicy : public AllStatic {
  public:
    typedef size_t ReferenceType;
    typedef size_t* OperationTypePtr;
  public:
    static void AddByOne(
      OperationTypePtr dest_ptr
      ) {
        ++(*dest_ptr);
    }
    static bool SubByOne(
      OperationTypePtr dest_ptr
      ) {
        XASSERT_SAY( (*dest_ptr) != 0,
          _xT("The reference is 0 , however calling SubByOne function!"));
        return --(*dest_ptr) == 0;
    }
    static void SetRef(
      OperationTypePtr dest_ptr,
      const ReferenceType& val ) {
        *dest_ptr = val;
    }
    static bool EmptyReference( const ReferenceType& ref ) {
      return ref == 0;
    }

  };

  template < typename T >
  class ThreadSafeStorePolicy {
  public:

    typedef AtomicOperation::Atomic_t ReturnType ;

    static void Store(
      volatile T& place , const T& val
      ) {
        StoreAndRelease(place,val);
    }

    static ReturnType Load(
      volatile const T& place
      ) {
        return (ReturnType)LoadAndAcquire(place);
    }
  };

  template < typename T >
  class CommonStorePolicy {
  public:
    typedef T ReturnType;
    static void Store(
      volatile T& place , const T& val
      ) {
        place = val;
    }

    static ReturnType Load(
      volatile const T& place
      ) {
        return (ReturnType)(place);
    }
  };


  template < typename T ,  typename OperationPolicy > 
  class ReferenceCount {
    typedef typename OperationPolicy::ReferenceType ReferenceType;
  protected:
    // XReference memory 
    EXPLICIT_CTOR(ReferenceCount ,bool as_one = true ) {
      if( as_one ) 
        OperationPolicy::SetRef((OperationPolicy::OperationTypePtr)(&reference_),1);
      else
        OperationPolicy::SetRef((OperationPolicy::OperationTypePtr)(&reference_),0);
    }

    void IncreaseRef() {
      OperationPolicy::AddByOne( (OperationPolicy::OperationTypePtr)(&reference_) );
    }

    bool DecreaseRef() {
      return OperationPolicy::SubByOne( (OperationPolicy::OperationTypePtr)(&reference_) ) ;
    }

  ~ReferenceCount() {
    // When reaching here the reference should be empty 
    // if not , error goes here
    XASSERT_SAY( OperationPolicy::EmptyReference( reference_ ) ,
      _xT("This reference should be 0 when the dtor executed , however now it is not zero!")
      );
  }
protected:
  ReferenceType reference() const {
    return reference_;
  }
  void set_reference( ReferenceType val ) {
    reference_ = val;
  }
private:
  ReferenceType reference_;
};

  // smart ptr 
  // a simple replacement of stl smart ptr
  // A XScoped_ptr<T> is like a T*, except that the destructor of scoped_ptr<T>
  // automatically deletes the pointer it holds (if any).
  // That is, XScoped_ptr<T> owns the T object that it points to.
  // Like a T*, a XScoped_ptr<T> may hold either NULL or a pointer to a T object.
  // Also like T*, XScoped_ptr<T> is thread-compatible, and once you
  // dereference it, you get the thread safety guarantees of T.
  //
  // The size of a scoped_ptr is small:
  // sizeof(XScoped_ptr<C>) == sizeof(C*)

 

template < typename C, typename DestroyPolicy >
  class ScopedPtr {
  public:

    // The element type
    typedef C element_type;

    // Constructor.  Defaults to initializing with NULL.
    // There is no way to create an uninitialized scoped_ptr.
    // The input parameter must be allocated with new.
    EXPLICIT_CTOR(ScopedPtr ,C* p = NULL) : ptr_(p) { }

    // Destructor.  If there is a C object, delete it.
    ~ScopedPtr() {
      if( ptr_ )
        DestroyPolicy::Destruct( ptr_ );
    }

    // Reset.  Deletes the current owned object, if any.
    // Then takes ownership of a new object, if given.
    // this->reset(this->get()) works.
    void Reset(C* p = NULL) {
      if (p != ptr_) {
        DestroyPolicy::Destruct( ptr_ );
        ptr_ = p;
      }
    }

    // Accessors to get the owned object.
    // operator* and operator-> will assert() if there is no current object.
    C& operator*() const {
      XASSERT(ptr_ != NULL);
      return *ptr_;
    }
    C* operator->() const  {
      XASSERT(ptr_ != NULL);
      return ptr_;
    }
    C* Get() const { return ptr_; }

    // Comparison operators.
    // These return whether two scoped_ptr refer to the same object, not just to
    // two different but equal objects.
    bool operator==(C* p) const { return ptr_ == p; }
    bool operator!=(C* p) const { return ptr_ != p; }

    // Swap two scoped pointers.
    void Swap(ScopedPtr& p2) {
      C* tmp = ptr_;
      ptr_ = p2.ptr_;
      p2.ptr_ = tmp;
    }

    // Release a pointer.
    // The return value is the current pointer held by this object.
    // If this object holds a NULL pointer, the return value is NULL.
    // After this operation, this object will hold a NULL pointer,
    // and will not own the object any more.
    C* Release()  {
      C* retVal = ptr_;
      ptr_ = NULL;
      return retVal;
    }

  private:
    C* ptr_;

    // Forbid comparison of scoped_ptr types.  If C2 != C, it totally doesn't
    // make sense, and if C2 == C, it still doesn't make sense because you should
    // never have the same object owned by two different scoped_ptrs.
    template <class C2,class D> bool operator==(ScopedPtr<C2,D> const& p2) const;
    template <class C2,class D> bool operator!=(ScopedPtr<C2,D> const& p2) const;

    DISALLOW_COPY_AND_ASSIGN( ScopedPtr );
  };

  // Free functions
  template <class C,class D>
  void Swap(ScopedPtr<C,D>& p1, ScopedPtr<C,D>& p2) {
    p1.Swap(p2);
  }

  template <class C,class D>
  bool operator==(C* p1, const ScopedPtr<C,D>& p2) {
    return p1 == p2.Get();
  }

  template <class C,class D>
  bool operator!=(C* p1, const ScopedPtr<C,D>& p2) {
    return p1 != p2.Get();
  }

  namespace {


    // Pointee is the place for storing the memory of the class as with its
    // reference count

    template < 
      typename T , 
      typename TypeDestruction , 
      typename ReferenceOperation 
    > class Pointee : public Allocator {
    public:
      typedef typename ReferenceOperation::ReferenceType ReferenceType;
      typedef T ValueType;
      typedef ValueType* ValueTypePtr;

      // Increase the reference count of this pointee
      // this will work as the ReferenceOperation says
      // so if the ReferenceOperation is atomic , definitely this
      // pointee will be thread safe
      void IncreaseRef() {
        ReferenceOperation::AddByOne( (ReferenceOperation::OperationTypePtr)(&reference_) );
      }
        
      // Decrease reference
      // if the reference count is 0 
      bool DecreaseRef() {
        if( ReferenceOperation::SubByOne( (ReferenceOperation::OperationTypePtr)(&reference_) ) ) {
          DestroyObject();
          return true;
        }
        return false;
      }

      ReferenceType reference() const {
        return reference_;
      }

      bool Empty() const {
        return ReferenceOperation::EmptyReference(reference_);
      }

      // Not using release and acquire order to deal with 
      // atomic stuff 
      Pointee( ValueTypePtr object_ptr , bool as_one = true ) {    
        object_ptr_= object_ptr;

        if( as_one ) 
          ReferenceOperation::SetRef((ReferenceOperation::OperationTypePtr)(&reference_),1);
        else
          ReferenceOperation::SetRef((ReferenceOperation::OperationTypePtr)(&reference_),0);
      }

      ValueTypePtr object_ptr() const {
        XASSERT(object_ptr_!=NULL);
        XASSERT(!ReferenceOperation::EmptyReference(reference_));
        return object_ptr_;
      }

    private:
      // Destroy the object itself
      // this will make the pointee lose the owner object
      void DestroyObject(){
        XASSERT(object_ptr_!=NULL);
        XASSERT(ReferenceOperation::EmptyReference(reference_));
        TypeDestruction::Destroy(object_ptr_);
        object_ptr_=(T*)NULL;
      }
      // the reference count
      ReferenceType reference_;
      // the specific T type
      ValueTypePtr object_ptr_;
      // pointee can not copy , of course 
      DISALLOW_COPY_AND_ASSIGN(Pointee);
    };

  } // namespace 

  // In thread stuff , we need a non-intrinsic smart ptr
  // namely the strong ptr ( also included some not owner ptr , weak ptr )
  // we have to code this !
  // also two version , one is thread safe , the other is not thread safe
  // A shared ptr in xge engine is the ptr has the ownership of the 
  // pointee
  template < 
    typename T , 
    typename TypeDestructor , 
    typename ReferenceOperation = ThreadSafeOperationPolicy 
  > class SharedPtr {
  public:
    // The default ctor
    // this ctor is used as null strong ptr 
    // it won't do anything about this strong ptr
    // simply initialized this ptr as NULL ptr
    // which means set the pointee ptr to NULL
    // it is thread safe 
    SharedPtr() ;

    // The explicit ctor
    // this ctor will initialize the ptr with pointee 
    // the pointee will be initialized 
    EXPLICIT_CTOR(SharedPtr,T* ptr);

    // The copy constructor
    // This will safely release the original ptr
    // and than do the swap operation

    SharedPtr( const SharedPtr& ptr );

    // The assign operator
    SharedPtr& operator = ( const SharedPtr& );

    ~SharedPtr() {
      DecRef();
    }

  public:
    bool operator == ( const SharedPtr& ptr ) const {
      return pointee_ == ptr.pointee_ ;
    }

    bool operator != ( const SharedPtr& ptr ) const {
      return !((*this) == ptr );
    }

  public:
    // Reset this ptr with that XSharedPtr::ptr
    void Reset( const SharedPtr& ptr ) {
      *this = ptr;
    }
    // Reset the pointer with another raw pointer
    void Reset( T* raw_ptr= NULL );
    // Test if this pointer is null
    bool Null() const {
      return pointee_ == NULL;
    }
  public:
    // Accessor : *
    T& operator* () const {
      XASSERT(!Null());
      return *(pointee_->object_ptr());
    }
    // Accessor : ->
    T* operator->() const {
      return Get();
    }
    // Accessor : Get()
    T* Get() const {
      XASSERT(!Null());
      return pointee_->object_ptr();
    }

    size_t reference() const {
      if(Null()) 
        return 0;
      return (size_t)(pointee_->reference());
    }

  private:
    typedef Pointee<T,TypeDestructor,ReferenceOperation> PointeeType;
    typedef PointeeType* PointeeTypePtr;

    void DecRef( const PointeeTypePtr val = NULL );
    void IncRef();

  private:
    PointeeTypePtr pointee_;
  };

  template < typename T , typename TypeDestructor , typename  ReferenceOperation >
  SharedPtr<T,TypeDestructor,ReferenceOperation>::SharedPtr() {
    pointee_= NULL;
  }

  template < typename T , typename TypeDestructor , typename  ReferenceOperation >
  SharedPtr<T,TypeDestructor,ReferenceOperation>::SharedPtr( T* ptr ) {
    pointee_ = new PointeeType(ptr);
  }

  template < typename T , typename TypeDestructor , typename  ReferenceOperation  >
  SharedPtr<T,TypeDestructor,ReferenceOperation>::SharedPtr( const SharedPtr& inst ) {
    SharedPtr& ref_ = const_cast<SharedPtr&>(inst);
    ref_.IncRef();
    pointee_= (const PointeeTypePtr)(ref_.pointee_);
  }


  template < typename T , typename TypeDestructor , typename  ReferenceOperation >
  SharedPtr<T,TypeDestructor,ReferenceOperation>& 
  SharedPtr<T,TypeDestructor,ReferenceOperation>::operator = 
    ( const SharedPtr& inst ) {
      //Check if it is the object itself
      if( this == &inst )
        return *this;

      DecRef(inst.pointee_);
      IncRef();
      return *this;
  }

  template < typename T , typename TypeDestructor , typename  ReferenceOperation >
  void SharedPtr<T,TypeDestructor,ReferenceOperation>::IncRef() {
    if( Null() ) 
      return;
    pointee_->IncreaseRef();
  }

  template < typename T , typename TypeDestructor , typename  ReferenceOperation >
  void SharedPtr<T,TypeDestructor,ReferenceOperation>::DecRef( const PointeeTypePtr val ) {
    if( Null() ) {
      if( val ) 
        pointee_ = val;
      return;
    }
    if( pointee_->DecreaseRef() ) {
      delete pointee_;
    }
    pointee_ = val;
  }

  template < typename T , typename TypeDestructor , typename  ReferenceOperation >
  void SharedPtr<T,TypeDestructor,ReferenceOperation >::Reset( T* raw_ptr ) {
    if( raw_ptr ) {
      DecRef( new PointeeType(raw_ptr) );
    } else {
      DecRef();
    }
  }
  // A simple default Destructor
  template < typename T > struct DefaultDestructor {
    static void Destroy(T* ptr) {
      delete ptr;
    }
  };


  // The following smart pointer is the intrinsic smart pointer which is used
  // to do the reference count by the user itself 

  template< typename ObjectType , typename TypeDestructor = DefaultDestructor<ObjectType> ,
    typename ReferenceOperation = CommonOperationPolicy > 
  class IntrinsicPointer  {
  public:
    // Increase the reference count 
    // If a certain class has derived this class , this will
    // every store of this pointer should call the grab function 
    // to increase the internal reference count .
    void Grab() {
      ReferenceOperation::AddByOne(&ref_count_);
    }
    // Call drop corresponding to the Grab function calling
    void Drop() {
      if( ReferenceOperation::SubByOne(&ref_count_) ) {
        TypeDestructor::Destroy(static_cast<ObjectType*>(this));
      }
    }
    // Get the reference count of this class 
    size_t reference_count() const {
      return static_cast<size_t>(ref_count_);
    }

    ~IntrinsicPointer() {
      XASSERT( ReferenceOperation::EmptyReference(ref_count_) );
    }
    // CTor of this class 
    IntrinsicPointer( bool init = true ) : ref_count_( init == true ? 1:0 ) {}
  private:
    typename ReferenceOperation::ReferenceType ref_count_;
  };

} // namespace xge_internal
}// namespace xge 
#endif // XGE_XSMART_PTR_H_